﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

static class ArrayTools2D
{
    public static void Print<T>(T[,] array)
    {
        string output = "";
        for (int y = 0; y < array.GetLength(0); y++)
        {
            for (int x = 0; x < array.GetLength(1); x++)
            {
                output += array[y, x] + " ";
            }
            output += "\n";
        }
        Debug.Log(output);
    }


    // Finds all mached lines on the board and returns a list of those lines
    // Iportant note: object stored in board must implement the System.IEquatable interface or override the Object.Equals method
    public static List<List<Point>> FindMatchingLines(int[,] array)
    {
        List<List<Point>> matches = new List<List<Point>>();

        // Tracing horizontal lines
        for (int y = 0; y < array.GetLength(0); y++)
        {
            for (int x = 0; x < array.GetLength(1) - 2; x++)
            {
                if (array[y, x] == 0) continue;
                Point position = new Point(x, y);
                List<Point> match = TraceHorizontalMatch(array, position);
                if (match.Count > 2)
                {
                    matches.Add(match);
                    x += match.Count - 1;
                }
            }
        }

        // Tracing vertical lines
        for (int x = 0; x < array.GetLength(1); x++)
        {
            for (int y = 0; y < array.GetLength(0) - 2; y++)
            {
                if (array[y, x] == 0) continue;
                Point position = new Point(x, y);
                List<Point> match = TraceVecticalMatch(array, position);
                if (match.Count > 2)
                {
                    matches.Add(match);
                    y += match.Count - 1;
                }
            }
        }

        return matches;
    }

	public static List<List<Point>> FindMatchingLines(int[,] array , int WithOutId)
	{
		List<List<Point>> matches = new List<List<Point>>();

		// Tracing horizontal lines
		for (int y = 0; y < array.GetLength(0); y++)
		{
			for (int x = 0; x < array.GetLength(1) - 2; x++)
			{
				if (array[y, x] == 0) continue;
				if (array [y, x] == WithOutId) continue;
				Point position = new Point(x, y);
				List<Point> match = TraceHorizontalMatch(array, position);
				if (match.Count > 2)
				{
					matches.Add(match);
					x += match.Count - 1;
				}
			}
		}

		// Tracing vertical lines
		for (int x = 0; x < array.GetLength(1); x++)
		{
			for (int y = 0; y < array.GetLength(0) - 2; y++)
			{
				if (array[y, x] == 0) continue;
				if (array [y, x] == WithOutId) continue;
				Point position = new Point(x, y);
				List<Point> match = TraceVecticalMatch(array, position);
				if (match.Count > 2)
				{
					matches.Add(match);
					y += match.Count - 1;
				}
			}
		}

		return matches;
	}

    // Returns list of identical items in a horizontal row
    private static List<Point> TraceHorizontalMatch(int[,] array, Point start)
    {
        List<Point> line = new List<Point>();
        line.Add(start);
        for (int x = start.x + 1; x < array.GetLength(1); x++)
        {
            if (array[start.y, x] == array[start.y, start.x])
            {
                line.Add(new Point(x, start.y));
            }
            else break;
        }
        return line;
    }

    // Returns list of identical items in a vertical row
    private static List<Point> TraceVecticalMatch(int[,] array, Point start)
    {
        List<Point> line = new List<Point>();
        line.Add(start);
        for (int y = start.y + 1; y < array.GetLength(0); y++)
        {
            if (array[y, start.x] == array[start.y, start.x])
            {
                line.Add(new Point(start.x, y));
            }
            else break;
        }
        return line;
    }

    // Swaps to position in the given array
    public static void Swap<T>(T[,] array, int x1, int y1, int x2, int y2)
    {
        if (IsOutOfBounds(array, x1, y1) || IsOutOfBounds(array, x2, y2))
        {
            Debug.LogError("Tried to swap points that are out of array bounds!");
            return;
        }
        T temp = array[y1, x1];
        array[y1, x1] = array[y2, x2];
        array[y2, x2] = temp;
    }

    public static void Swap<T>(T[,] array, Point p1, Point p2)
    {
        Swap<T>(array, p1.x, p1.y, p2.x, p2.y);
    }

    // Check wether to points can be swapped => they are located near to each other, diagonal cases excluded
    // Important note: this method does not check wether the points is out of the board bounds
    public static bool IsItemsSwappable(int x1, int y1, int x2, int y2)
    {
        // Using little trick with two vectors to avoid big, fat IF statement
        return Vector2.Distance(new Vector2(x1, y1), new Vector2(x2, y2)) == 1.0f ? true : false;
    }

    public static bool IsItemsSwappable(Point p1, Point p2)
    {
        return IsItemsSwappable(p1.x, p1.y, p2.x, p2.y);
    }

    // Checks whether spesified position is out of bound of the given array
    public static bool IsOutOfBounds<T>(T[,] array, int x, int y)
    {
        return !((x >= 0 && x < array.GetLength(1)) && (y >= 0 && y < array.GetLength(0)));
    }

    public static bool IsOutOfBounds<T>(T[,] array, Point point)
    {
        return IsOutOfBounds<T>(array, point.x, point.y);
    }
}